/**
 * @fileName webpack.config.js
 * @description based.config|基础配置
 * @param mode|开发模式
 * @param entry|入口文件路径
 * @param output|打包输出配置
 */
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const FileListPlugin = require('./plugin/FileListPlugin');
const RemoveCommentPlugin = require('./plugin/RemoveCommentPlugin');
const zipDisPlugin = require('./plugin/zipDisPlugin');
const { isProd } = require('../src/utils/is');
const BundleAnalyzerPlugin =
  require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
// const WorkboxPlugin = require('workbox-webpack-plugin'); // 离线缓存
module.exports = {
  entry: {
    // main: { import: path.resolve(__dirname, '../src/index.jsx'), dependOn: 'react-vendors' },
    // 'react-vendors': ['react', 'react-dom'],

    main: { import: path.resolve(__dirname, '../src/index.jsx') },
  },
  output: {
    filename: isProd()
      ? '[name].[contenthash].bundle.js'
      : '[name].[fullhash:8].bundle.js',
    chunkFilename: isProd()
      ? '[name].[contenthash].chunk.js'
      : '[name].[fullhash:8].chunk.js',
    path: path.resolve(__dirname, '../dist'), // 打包后的目录
    clean: true, // 在生成文件之前清空 output 目录
    // TODO: CDN配置失败
    publicPath: '/my-app/', // 打包后文件路径开发环境会多一个/my-app/，生产环境
    // asyncChunks: true, // 创建按需加载的异步 chunk。
  },
  externals: {
    lodash: '_',
  },
  /**
   * @description resolve|解析配置
   * @param {String} extensions |文件后缀扩展
   * @param {object} alias |别名配置
   */
  resolve: {
    extensions: ['.jsx', '.js', '.tsx', '.ts'], // 如使用Ts测加上'.tsx', '.ts'
    alias: {
      '@': path.join(__dirname, '../src'),
      '@components': path.join(__dirname, '../src/components'),
      '@utils': path.join(__dirname, '../src/utils'),
      '@assets': path.join(__dirname, '../src/assets'),
      '@pages': path.join(__dirname, '../src/pages'),
    },
  },
  // 性能提示TODO: 优化
  performance: {
    hints: 'warning',
    //入口起点的最大体积
    maxEntrypointSize: 50000000,
    //生成文件的最大体积
    maxAssetSize: 30000000,
    //只给出 js 文件的性能提示
    assetFilter: function (assetFilename) {
      return assetFilename.endsWith('.js');
    },
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, '../public/index.html'),
    }),
    // 但需要注意的是独立抽取出来的.css一般使用于生产环境，而对于开发环境，为了方便热更新HRM使用 style-loader效果可能更好。
    new MiniCssExtractPlugin({
      filename: 'static/css/[name].[contenthash:8].css',
    }),
    // new BundleAnalyzerPlugin(), // assest 文件分析

    // new WorkboxPlugin.GenerateSW({
    //   // 这些选项帮助快速启用 ServiceWorkers
    //   // 不允许遗留任何“旧的” ServiceWorkers
    //   clientsClaim: true,
    //   skipWaiting: true,
    // }),
    // new FileListPlugin(),
    // new RemoveCommentPlugin(),
    // new zipDisPlugin({ filename: 'dist' }),
  ],
  module: {
    // 遗留模块没有 AMD/CommonJS 版本，但你也想将他们加入 dist 文件，则可以使用 noParse 来标识出这个模块。
    // 这样就能使 webpack 将引入这些模块，但是不进行转化(parse)，以及不解析(resolve) require() 和 import 语句。
    // 这种用法还会提高构建性能。
    noParse: /jquery/,
    // unsafeCache: true, // 缓存,此模块的来自 node_modules，则值为 true，否则为 false。
    rules: [
      {
        test: /.(js|jsx|ts|tsx)$/, // ts : /.(ts|tsx)$/
        include: path.resolve(__dirname, '../src'),
        exclude: /node_modules/, //排除内容不解析
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'],
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              modules: {
                localIdentName: '[name]__[local]__[hash:base64:5]',
              },
            },
          },
          'postcss-loader',
          'less-loader',
        ],
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          isProd() ? MiniCssExtractPlugin.loader : 'style-loader',
          'css-loader',
          'postcss-loader',
          'sass-loader',
        ],
      },

      //图像
      {
        test: /.(png|jpg|jepg|git|svg)$/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 10 * 1024, //限制在 10kb
          },
        },
        generator: {
          filename: 'static/images/[name][ext]',
        },
      },
      // 视频
      {
        test: /.(mp4|mp3|webm)$/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 10 * 1024, // 10kb
          },
        },
        generator: {
          // 自定义某些模块的输出路径
          filename: 'static/medias/[name][ext]',
        },
      },
      //字体
      {
        test: /.(woff2|eot|ttf|otf)$/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 10 * 1024,
          },
        },
        generator: {
          filename: 'static/fonts/[name][ext]',
        },
      },
    ],
  },
  devServer: {
    // 开启 HMR 特性
    hot: true,
  },
  optimization: {
    usedExports: true,
    // minimize: true,
    minimizer: [
      new CssMinimizerPlugin({
        parallel: true, // 并行压缩
      }),
      new TerserPlugin({
        parallel: true, // 并行压缩
        terserOptions: {
          // compress:true
          compress: {
            pure_funcs: ['console.log', 'console.warn'], //去除
          },
        },
      }),
    ],
    splitChunks: {
      // 表明将选择哪些 chunk 进行优化，设置为 all 可能特别强大，因为这意味着 chunk 可以在异步和非异步 chunk 之间共享。
      chunks: 'all',
      // 生成 chunk 的最小体积（以 bytes 为单位）
      // minSize: 20000,
      // 告诉 webpack 尝试将大于 maxSize 个字节的 chunk 分割成较小的部分,这些较小的部分在体积上至少为 minSize
      maxSize: 1 * 1024 * 1024,
      // 通过确保拆分后剩余的最小 chunk 体积超过限制来避免大小为零的模块， 'development' 模式 中默认为 0。
      // 对于其他情况，splitChunks.minRemainingSize 默认为 splitChunks.minSize 的值，因此除需要深度控制的极少数情况外，不需要手动指定它。
      // minRemainingSize: 0,

      minChunks: 1, // 共享模块至少被引用一次
      maxAsyncRequests: 30, // 按需加载时的最大并行请求数。
      maxInitialRequests: 30, // 入口点的最大并行请求数。
      enforceSizeThreshold: 50000,
      // 由于在这个示例中单个 HTML 页面有多个入口，所以添加了 optimization.runtimeChunk: 'single' 配置，否则可能会遇到 这个问题。查看 代码分割 章节了解更多细节
      // runtimeChunk: 'single',
      // cacheGroups缓存组可以继承和/或覆盖来自 splitChunks.* 的任何选项。
      cacheGroups: {
        // 创建一个 vendors chunk，其中包括整个应用程序中 node_modules 的所有代码。
        //  警告
        // 这可能会导致包含所有外部程序包的较大 chunk。建议仅包括你的核心框架和实用程序，并动态加载其余依赖项。
        vendors: {
          // filename: 'js/[name]/bundle.js',
          chunks: 'all',
          // 一个模块可以属于多个缓存组。优化将优先考虑具有更高 priority（优先级）的缓存组。
          priority: 1,
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          minChunks: 1,
          maxSize: 1 * 1024 * 1024,
        },

        // TODO: 以下两个配置没有效果，需要进一步研究
        // vendor: {
        //   chunks: 'all', // 表示只从入口模块中进行代码分割。其他可选值包括 'async'（异步模块）和 'all'（所有模块）。
        //   priority: 10, // 一个模块可以属于多个缓存组。优化将优先考虑具有更高 priority（优先级）的缓存组。默认组的优先级为负，以允许自定义组获得更高的优先级（
        //   test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/, // 当 webpack 处理文件路径时，它们始终包含 Unix 系统中的 / 和 Windows 系统中的 \。这就是为什么在 {cacheGroup}.test 字段中使用 [\\/] 来表示路径分隔符的原因。
        //   name: 'vendor',
        //   chunks: 'all',
        //   // 告诉 webpack 忽略 splitChunks.minSize、splitChunks.minChunks、splitChunks.maxAsyncRequests 和 splitChunks.maxInitialRequests 选项，并始终为此缓存组创建 chunk。
        //   // enforce: true,
        // },

        // default: {
        //   chunks: 'all', // 这个配置的目的是防止重复引用的代码被多次打包，而是将其放入一个共享的 chunk 中，以减小最终输出文件的大小。
        //   priority: -1, // 默认值为  -20
        //   name: 'default',
        //   minChunks: 2, // 表示一个模块至少被引用两次才会被拆分出来，也就是说，如果一个模块在两个或更多入口文件中被引用，它将被拆分出来并放入 'commons' chunk。
        //   // enforce: true, // 默认false 告诉 webpack 忽略 splitChunks.minSize、splitChunks.minChunks、splitChunks.maxAsyncRequests 和 splitChunks.maxInitialRequests 选项，并始终为此缓存组创建 chunk。
        //   // layer: (module) => {  // 按模块层将模块分配给缓存组。
        //   //   // 返回模块的层级名称
        //   //   if (module.resource.includes('node_modules')) {
        //   //     return 'vendors'; // 如果模块位于 node_modules，属于 vendor 层级
        //   //   } else {
        //   //     return 'default'; // 否则属于 default 层级
        //   //   }
        //   // },
        //   // reuseExistingChunk: true,// 默认true 如果当前 chunk 包含已从主 bundle 中拆分出的模块，则它将被重用，而不是生成新的模块。这可能会影响 chunk 的结果文件名。
        // },
      },
    },
  },
};
